package com.t.bricks.bussiness.component.lucene;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;

import com.t.bricks.bussiness.util.lucene.LuceneFactory;
import com.t.bricks.model.Log;
import com.t.bricks.model.MsgEmity;
import com.t.bricks.model.OrderInfoList;
import com.t.bricks.utils.function.reflect.ReflectUtil;
import com.t.bricks.utils.spring.SpringContextUtil;

/**
 * 搜索引擎切面基类,整合了常用的功能
 */
public abstract class LuceneAspectBase {
	
	/**
	 * 搜索引擎文件主路径
	 */
	protected static String lucenePath = String.format("%s/temp/lucene/",
			System.getProperty("user.dir").replaceAll("\\\\", "/"));

	/**
	 * 搜索引擎库根路径配置
	 */
	@Value("${app.lucene.index.path.root:配置文件中的搜索引擎根路径app.lucene.path.root未设置}")
	protected String sLuceneIndexPath;

	/**
	 * 是否允许搜索引擎进行AOP操作(总开关)
	 */
	@Value("${app.lucene.aop.can:true}")
	protected boolean isCanAop;

	/**
	 * 定义切入点，切入点为'com.t.bricks.bussiness.db.dao.*.impl.*DaoImpl.*(..)'下的所有函数,
	 * 由子类定义
	 */
	//@Pointcut(value = "execution(* com.t.bricks.bussiness.db.dao.*.impl..*DaoImpl.*(..))")
	protected abstract void doLuceneAspect();

	/**
	 * 对象同步锁集合
	 */
	private static Map<String, Object> mapLock = null;
	
	/**
	 * 对象同步锁引用计数值集合,当值为0时同时清理'对象同步锁集合'
	 */
	private static Map<String, Integer> mapLockCount = null;

    /**
     * 前置通知：在目标方法执行前调用
     */
    @Before("doLuceneAspect()")
	public void begin(JoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		String methodName = signature.getMethod().getName();
		
		Log.debug("搜索引擎切面介入'", joinPoint.getTarget().getClass().getName(), ".", methodName, "'方法!");
	}

    /**
     * 后置/最终通知：无论目标方法在执行过程中出现一切都会在它之后调用
     */
    @After("doLuceneAspect()")
    public void after() {
    	//System.out.println("在切入点的方法执行后");
    }

    /**
     * 异常通知：目标方法抛出异常时执行,如果内部已经进行了异常捕捉,则不会进入afterThrowing
     */
    @AfterThrowing("doLuceneAspect()")
    public void afterThrowing(JoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		String methodName = signature.getMethod().getName();
		
		Log.debug("搜索引擎切面介入'", joinPoint.getTarget().getClass().getName(), ".", methodName, "'方法有异常发生!");
	}

    /**
     * 消息循环
     * @param joinPoint
     * @return
     * @throws Throwable
     */
	@Around("doLuceneAspect()")
	public abstract Object doAround(ProceedingJoinPoint joinPoint) throws Throwable;
	
	/**
	 * 建立搜索库
	 * @param joinPoint
	 * @param sLuceneIndexPath 搜索库路径
	 * @return
	 */
	protected static MsgEmity crateLuceneCore(JoinPoint joinPoint, String sLuceneIndexPath) {
		String core = getCore(joinPoint, sLuceneIndexPath);
		if (null == core || "".equals(core.trim())) {
			return MsgEmity.err(1001, "未能找到对应的库");
		}
		
		File file = new File(core);
		if (file.exists()) {
			return MsgEmity.success(6999, "'", core, "'库原本存在!");
		}

		Object lockObj = null;//锁标识
		synchronized (LuceneAspectBase.class) {
			if (null == mapLock) {
				mapLock = new ConcurrentHashMap<String, Object>(5);
				mapLockCount = new ConcurrentHashMap<String, Integer>(5);
			}

			lockObj = mapLock.get(sLuceneIndexPath);//获取对象锁
			if (null == lockObj) {
				lockObj = new Object();
				mapLock.put(sLuceneIndexPath, lockObj);
				mapLockCount.put(sLuceneIndexPath, 1);//计数值
			} else {
				mapLockCount.put(sLuceneIndexPath, mapLockCount.get(sLuceneIndexPath) + 1);//计数值+1
			}
		}
		
		synchronized(lockObj) {
			try {
				file = new File(core);
				if (file.exists()) {
					return MsgEmity.success(6999, "'", core, "'库已经存在!");
				}
			
				String beanName = getDaoBeanName(joinPoint);
				
				MsgEmity me = null;
				if ("commonDaoImpl".equals(beanName)) {
					Object[] methodArgs = joinPoint.getArgs();//这是被监听的接口的参数
					Class<?> entityClass = methodArgs[0].getClass();
					me = SpringContextUtil.doMethod(
							beanName, "findBySearch",
							new Class<?>[]{ String.class, OrderInfoList.class, Class.class },
							new Object[]{ " 2=2 ", null, entityClass });
				} else {
					me = SpringContextUtil.doMethod(
							beanName, "findBySearch",
							new Class<?>[]{ String.class, OrderInfoList.class },
							new Object[]{ " 2=2 ", null });
				}
				
				if (null == me || !me.isSuccess()) {
					Log.error("搜索引擎读取数据失败!");
					return MsgEmity.err(6001, "建'", core, "'库失败!");
				}
				
				me = me.getData();//SpringContextUtil.doMethod返回的data里面是MsgEmity
				if (!me.isSuccess()) {
					return me;
				}
				
				List<?> list = me.getData();
				if (list.size() < 1) {
					return MsgEmity.err(6002, "没有数据，建'", core, "'库失败!");
				}
				
				String idName = "sId";
				if (!ReflectUtil.hasAttr(list.get(0), "sId", true)) {//可能是自增长,此时的主键名称应该是iId
					if (ReflectUtil.hasAttr(list.get(0), "iId", true)) {
						idName = "iId";
					} else if (ReflectUtil.hasAttr(list.get(0), "uId", true)) {
						idName = "uId";
					} else {
						idName = "sId";
					}
				}
				
				me = LuceneFactory.add(idName, list, core);
				if (!me.isSuccess()) {
					return me;
				}
			} finally{
				mapLockCount.put(sLuceneIndexPath, mapLockCount.get(sLuceneIndexPath) - 1);//计数值-1
				if (mapLockCount.get(sLuceneIndexPath) < 1) {//计数值为0,剔除
					mapLockCount.remove(sLuceneIndexPath);
					mapLock.remove(sLuceneIndexPath);
					
					if (mapLock.size() == 0) {
						mapLock = null;
						mapLockCount = null;
					}
				}
			}
		}
		
		return MsgEmity.success(6999, "建'", core, "'库成功!");
	}
	
	/**
	 * 获取库文件地址
	 * @param joinPoint 切面方法信息
	 * @param sLuceneIndexPath 搜索库路径
	 * @return
	 */
	protected static String getCore(JoinPoint joinPoint, String sLuceneIndexPath) {
		String core = joinPoint.getTarget().getClass().getSimpleName();//joinPoint.getTarget() 被代理对象; joinPoint.getThis();代理对象
		core = core.substring(0, core.length() -7);//FollowUpDaoImpl-->FollowUp
		
		if ("Common".equalsIgnoreCase(core)) {//通用方法不能作为索引库
			core = getCoreByParam(joinPoint);
			
			if (null == core || "".equals(core.trim())) {
				return null;
			}
		}
		
		if ("配置文件中的搜索引擎根路径app.lucene.path.root未设置".equals(sLuceneIndexPath.trim())) {
			sLuceneIndexPath = new StringBuilder(lucenePath).append("index/").toString();
		}
		
		core = new StringBuilder(sLuceneIndexPath).append(core).toString();
		
		return core;
	}

	/**
	 * 通过参数获取库名
	 * 主要用于获取Common操作的情况
	 * @param joinPoint 切面方法信息
	 * @return
	 */
	protected static String getCoreByParam(JoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();
		if (null == args || args.length < 1) {
			return null;
		}
		
		if (args.length == 1 && Class.class.equals(args[0].getClass())) {
			Class<?> entityClass = (Class<?>)args[0];//一定是数据库实体类的类定义
			Class<?> baseClass = ReflectUtil.findBaseClass(entityClass);
			String name = baseClass.getSimpleName();
			if (name.endsWith("Base")) {
				name = name.substring(0, name.lastIndexOf("Base"));
			}
			
			return name;
		}

		if (args.length == 1 && !Class.class.equals(args[0].getClass())) {
			Class<?> entityClass = args[0].getClass();//一定是数据库实体类的实体对象
			Class<?> baseClass = ReflectUtil.findBaseClass(entityClass);
			String name = baseClass.getSimpleName();
			if (name.endsWith("Base")) {
				name = name.substring(0, name.lastIndexOf("Base"));
			}
			
			return name;
		}
		
		if (Class.class.equals(args[args.length - 1].getClass())) {
			Class<?> entityClass = (Class<?>)args[args.length - 1];//最后一个一定是实体类的类定义
			Class<?> baseClass = ReflectUtil.findBaseClass(entityClass);
			String name = baseClass.getSimpleName();
			if (name.endsWith("Base")) {
				name = name.substring(0, name.lastIndexOf("Base"));
			}
			
			return name;
		}
		
		for (Object object : args) {//查找有类定义的参数做为库名
			if (!Class.class.equals(object.getClass())) {
				continue;
			}
			
			Class<?> entityClass = (Class<?>)object;//最后一个一定是实体类的类定义
			Class<?> baseClass = ReflectUtil.findBaseClass(entityClass);
			String name = baseClass.getSimpleName();
			if (name.endsWith("Base")) {
				name = name.substring(0, name.lastIndexOf("Base"));
			}
			
			return name;
		}

		return null;//没找到
	}
	
	/**
	 * 取返回的泛型的类型
	 * @param msgEmity
	 * @return
	 */
	protected static Class<?> getGenericClass(MsgEmity msgEmity) {
		if (null == msgEmity || null == msgEmity.getData()) {
			return null;
		}
		
		return msgEmity.getData().getClass();
	}
	
	/**
	 * 取处理事件的Dao实现类名称
	 * @param joinPoint
	 * @return
	 */
	protected static String getDaoBeanName(JoinPoint joinPoint) {
		String beanName = joinPoint.getTarget().getClass().getSimpleName();//joinPoint.getTarget() 被代理对象; joinPoint.getThis();代理对象

		if (beanName == null || beanName.length() == 0) {
			return beanName;
		}
		
		if (beanName.length() > 1
				&& Character.isUpperCase(beanName.charAt(1))
				&& Character.isUpperCase(beanName.charAt(0))) {
			return beanName;//根据springmvc命名规则,如果两个或以上的大写字母开头则直接返回
		}
		
		char chars[] = beanName.toCharArray();
		chars[0] = Character.toLowerCase(chars[0]);
		
		return new String(chars);
	}
	
	/**
	 * 取主键信息
	 * @param entityClass
	 * @return
	 */
	protected static String getKeyName(Class<?> entityClass) {
		if (ReflectUtil.hasAttr(entityClass, "sId", true)) {
			return "sId";
		}
		
		if (ReflectUtil.hasAttr(entityClass, "iId", true)) {
			return "iId";
		}
		
		if (ReflectUtil.hasAttr(entityClass, "uId", true)) {
			return "uId";
		}
		
		return null;
	}
	
	/**
	 * 取主键信息
	 * @param entity
	 * @return
	 */
	protected static Map<String, String> getKeyInfo(Object entity) {
		String idName = "sId";
		Object idObj = ReflectUtil.getAttribute(entity, idName);//通过反射获取名称为"sId"的实体主键
		if (null != idObj) {//可能是自增长,此时的主键名称应该是iId
			Map<String, String> map = new HashMap<String, String>(2);
			map.put("idName", idName);
			map.put("idObj", idObj.toString());
			return map;
		}

		idName = "iId";
		idObj = ReflectUtil.getAttribute(entity, idName);//通过反射获取名称为"iId"的实体主键
		if (null != idObj) {
			Map<String, String> map = new HashMap<String, String>(2);
			map.put("idName", idName);
			map.put("idObj", idObj.toString());
			return map;
		}

		idName = "uId";
		idObj = ReflectUtil.getAttribute(entity, idName);//通过反射获取名称为"iId"的实体主键
		if (null == idObj) {
			return null;
		}
		
		Map<String, String> map = new HashMap<String, String>(2);
		map.put("idName", idName);
		map.put("idObj", idObj.toString());
		
		return map;
	}
	
	/**
	 * 取主键名称
	 * @param entityClass
	 * @return
	 */
	protected static String getIdName(Class<?> entityClass) {
		String idName = "sId";
		if (ReflectUtil.hasAttr(entityClass, idName, true)) {
			return idName;
		}
		
		idName = "uId";
		if (ReflectUtil.hasAttr(entityClass, idName, true)) {
			return idName;
		}
		
		idName = "iId";
		if (ReflectUtil.hasAttr(entityClass, idName, true)) {
			return idName;
		}
		
		return "sId";
	}

}
